<?php
/**
 * This file contains:
 * - class to send requests to a url
 *
 * @author Sven Ruttloff, www.sruttloff.de <service@sruttloff.de>
 */

/**
 * http request handler
 *
 * @package request
 * @version 1
 */
class httprequest {
    /**
     * @var 	string 	hostname for service
     */
    private $HOST = null;
    /**
     * @var 	int			port number for host
     */
    private $PORT = 80;
    /**
     * @var string referer
     */
    private $REFERER = null;
    /**
     * @var string connection charset
     */
    private $CHARSET = 'utf-8';
    /**
     * close connection after submission
     *
     * @var bool true = connection close
     */
    private $CONNECTIONCLOSE = true;
    /**
     * if a response is 301 or 302 follow this redirection
     *
     * @var bool default false
     */
    private $FOLLOWREDIRECT = true;
    /**
     * last request that was send
     *
     * @var string last request
     */
    private $LASTREQUEST = null;

    /**
     * @var 	bool		if true you will see some actions in browser
     */
    public $DEBUG = false;
    /**
     * class constructor
     *
     */
    public function __construct()
    {

    }
    /**
     * return last request that was send
     * @return string
     */
    public function getLastRequest()
    {
        return $this->LASTREQUEST;
    }
    /**
     * send a get request to a specified url at host
     *
     * @param		string    	url
     * @return     	string		request result
     */
    public function Get($uri)
    {
        return $this->request('GET', $uri, '');
    }

    /**
     * put a request to a specified url at host with content
     *
     * @param		string    	url
     * @param		string		content to put
     * @return     	string		request result
     */
    public function Put($uri, $body)
    {
        return $this->request('PUT', $uri, $body);
    }

    /**
     * post a request to a specified url at host with content
     *
     * @param		string    	url
     * @param		string		content to post
     * @return     	string		request result
     */
    public function Post($uri, $body)
    {
        return $this->request('POST', $uri, $body);
    }

    /**
     * delete a request to a specified url at host with content
     *
     * @param		string		Method delete
     * @param		string    	url
     * @param		string		empty string
     * @return     	string		request result
     */
    public function Delete($uri)
    {
        return $this->request('DELETE', $uri, '');
    }

    private function debug ($msg = null)
    {
        if (!$this->DEBUG)
            return false;
        else
            echo $msg . "\r\n<br />";
    }
    /**
     * extract hostname from url and store internal
     *
     * @param string url include hostname
     * @return string hostname
     */
    public function setHostNameByUri ($uri = null)
    {
        $parsedURL = parse_url($uri);
        $hostName = $parsedURL['host'];
        $hostName = strtolower($hostName);
        if (!$hostName)
            throw new Exception ('No Hostname found!');
        $this->HOST = $hostName;
        return $this->HOST;
    }
    /**
     * return only uri, without hostname but included get param
     *
     * @param string uri
     * @return clean uri
     */
    private function ParseUri ($uri)
    {
        $parsedURL = parse_url($uri);
        return $parsedURL['path'] . ($parsedURL['query'] ? '?' . $parsedURL['query'] : '');
    }
    /**
     * send a request to the server
     *
     * @param		string		Method
     * @param		string    	url
     * @param		string		request string
     * @return     	string		request result
     */
    private function request($method, $uri, $body)
    {
        #if (!$this->HOST)
            $this->setHostNameByUri($uri);
        // open socket
        $this->debug("Connection to: " . $this->HOST . " at port: " . $this->PORT);
        $sd = fsockopen($this->HOST, $this->PORT, $errno, $errstr, 5);
        if (!$sd)
        {
            throw new Exception ('No connect to Host');
        }
        else
        {
            $this->debug ("Connected. use method: " . $method . " with uri: " . $this->ParseUri($uri));
            //send request to server
            fputs($sd, $this->make_string($method, $this->ParseUri($uri), $body));
            //read answer
            $result = (string) null;
            while(!feof($sd))
            {
                $result .= fgets($sd, 8192);
            }

            fclose($sd);
        }
        $this->response = $result;
        // is a redirect
        if (($this->ResponseCode() == '301' || $this->ResponseCode() == '302') && $this->FOLLOWREDIRECT )
        {
            // extract redirect uri
            preg_match('=location:(.+)\n=siU', $this->response, $temp);
            // check if complete location is set
            if (!preg_match('=^/.+/=', $temp[1]))
                $temp[1] = dirname($this->ParseUri($uri)) . '/' . $temp[1];
            $redirecturi = trim($temp[1]);
            // extract vars
            #throw new Exception ($this->response . '-' . $redirecturi);
            #echo "old::" . $this->response;
            $this->response = $this->Get('http://' . $this->HOST . '/' . $redirecturi);
            #echo "<b>new::</b>" . $this->response;
        }

        return $this->response;
    }
    /**
     * return the response code from nic-direct
     *
     * <p>This could a numerical or string response, like handle numbers.
     * In case of error. You get a string like &407. The meaning is descript at xmlapi.nic-direct.de
     * </p>
     *
     * @return		mixed
     */
    function ResponseCode ()
    {
        return httpresponse::code($this->response);
    }
    /**
     * This returns the complete response body from nic-direct
     *
     * @return 	string
     */
    function ResponseBody ()
    {
        return httpresponse::body($this->response);
    }

    /**
     * create request string
     *
     * @param		string		Method
     * @param		string    	url
     * @param		string		request string
     * @return     	string		well formed request string
     */
    private function make_string($method, $uri, $body)
    {
        $str = (string) null;
        $str .= strtoupper($method) . " $uri HTTP/1.1\r\n";
        $str .= "Host: " . $this->HOST . "\r\n";
        $str .= "User-Agent: phpmy\r\n";
        if ($this->REFERER)
            $str .=  "Referer: " . $this->REFERER. "\r\n";
        if (strtoupper($method) == 'POST')
            $str .= "Content-type: application/x-www-form-urlencoded\r\n";
        else
            $str .= "Content-type: text/xml; charset=" . $this->CHARSET . "\r\n";
        if ($this->CONNECTIONCLOSE)
            $str .= "Connection: close\r\n";
        else
            $str .= "Connection: Keep-Alive\r\n";
        //header: body size ... if any
        if ($body)
        {
            $str .= "Content-Length: " . strlen($body) . "\r\n";
        }
        $str .= "\r\n";
        //append body ... if any
        if ($body)
        {
            $str .= $body;
        }
        $this->LASTREQUEST = $str;
        return $str;
    }
}

/**
 * http response handler
 *
 * @package request
 * @version 1
 */
class httpresponse
{
    public $resp;
    private $RESP = null;

    /**
     *
     * extract response code from response header
     *
     * @param       string      response header to eval.
     * @return      string      response code eg.200 OK or a failure code eg. &01
     */
    function code($resp = null)
    {
        if ($resp)
            $this->RESP = $resp;
        preg_match('/HTTP\/[10\.]+\s+([0-9]+)/', $this->RESP, $code);
        return $code[1];
    }

    /**
     *
     * extract response body from response
     *
     * @param       string      response to eval.
     * @return      string      response body
     */
    function body($resp = null)
    {
        if ($resp)
            $this->RESP = $resp;

        //body is between two empty lines for "chunked" encoding
        $chunk = preg_split('|(?:\r?\n){2}|m', $this->RESP, 2);

        $decBody = '';
        $body = $chunk[1];

        while (trim($body)) {
            if (! preg_match("/^([\da-fA-F]+)[^\r\n]*\r\n/sm", $body, $m)) {
                return $body;
                #throw new Exception("Error parsing body - doesn't seem to be a chunked message");
            }

            $length = hexdec(trim($m[1]));
            $cut = strlen($m[0]);

            $decBody .= substr($body, $cut, $length);
            $body = substr($body, $cut + $length + 2);
        }

        return $decBody;
    }
}